From b1d62eecbba151d4dde4f803e87c3c82ca934fab Mon Sep 17 00:00:00 2001 From: Keir Fraser Date: Thu, 15 Apr 2010 11:36:20 +0100 Subject: [PATCH] acpi sleep: Rearrange code for entering system sleep states. We cannot freeze_domains in hypercall-continuation context any more, since that is a softirq context which can interrupt an arbitrary vcpu. Hence sleeping all vcpus in that context can easily deadlock (against the vcpu we interrupted). So rearrange the code to freeze_domains before calling continue_hypercall_on_cpu(). Signed-off-by: Keir Fraser --- xen/arch/x86/acpi/power.c | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/xen/arch/x86/acpi/power.c b/xen/arch/x86/acpi/power.c index e6847b3e4f..0252ff4fbc 100644 --- a/xen/arch/x86/acpi/power.c +++ b/xen/arch/x86/acpi/power.c @@ -149,15 +149,7 @@ static int enter_state(u32 state) int error; unsigned long cr4; - if ( (state <= ACPI_STATE_S0) || (state > ACPI_S_STATES_MAX) ) - return -EINVAL; - - if ( !spin_trylock(&pm_lock) ) - return -EBUSY; - - printk(XENLOG_INFO "Preparing system for ACPI S%d state.", state); - - freeze_domains(); + BUG_ON(!spin_is_locked(&pm_lock)); disable_nonboot_cpus(); if ( num_online_cpus() != 1 ) @@ -246,6 +238,9 @@ static long enter_state_helper(void *data) */ int acpi_enter_sleep(struct xenpf_enter_acpi_sleep *sleep) { + int rc; + u32 state; + if ( !IS_PRIV(current->domain) || !acpi_sinfo.pm1a_cnt_blk.address ) return -EPERM; @@ -258,14 +253,31 @@ int acpi_enter_sleep(struct xenpf_enter_acpi_sleep *sleep) return -EINVAL; } - if ( sleep->flags ) + state = sleep->sleep_state; + if ( sleep->flags || + (state <= ACPI_STATE_S0) || (state > ACPI_S_STATES_MAX) ) return -EINVAL; + if ( !spin_trylock(&pm_lock) ) + return -EBUSY; + acpi_sinfo.pm1a_cnt_val = sleep->pm1a_cnt_val; acpi_sinfo.pm1b_cnt_val = sleep->pm1b_cnt_val; - acpi_sinfo.sleep_state = sleep->sleep_state; + acpi_sinfo.sleep_state = state; + + printk(XENLOG_INFO "Preparing system for ACPI S%d state.", state); + + freeze_domains(); + + rc = continue_hypercall_on_cpu(0, enter_state_helper, &acpi_sinfo); + if ( rc ) + { + /* Continuation will not execute: undo our own work so far. */ + thaw_domains(); + spin_unlock(&pm_lock); + } - return continue_hypercall_on_cpu(0, enter_state_helper, &acpi_sinfo); + return rc; } static int acpi_get_wake_status(void) -- 2.30.2